# -*- coding: utf-8 -*-
__author__ = 'Rui & Brazeta'

import re
import csv

class Graph:

    #Construtor
    def __init__(self):
        """
        Constructor for the class
        """
        self._spo = []
        pass

    def add(self, sub, pred, obj):
        self._addToIndex(self._spo, sub, pred, obj)

    def _addToIndex(self, Index, a, b, c):
        chave = str.format("%s,%s,%s" %(a, b, c))
        if Index.__contains__(chave): return
        Index.append(chave)
        return

    def triples(self, sub, pred, obj):

        # I have all the info
        if sub != None and pred != None and obj != None:

            if obj.startswith(">"):
                chave = str.format("%s,%s," %(sub, pred))
                valorBase = float(obj.replace('>', ''))
                if not Graph.is_float_try(valorBase): return
                for elemento in self._spo:
                    if(not elemento.startswith(chave)): continue
                    objecto = elemento.split(',')[2]
                    if not Graph.is_float_try(objecto): continue
                    if(not float(objecto) > float(valorBase)): continue
                    yield(elemento.split(','))
                return

            elif obj.startswith("<"):
                chave = str.format("%s,%s," %(sub, pred))
                valorBase = float(obj.replace('<', ''))
                if not Graph.is_float_try(valorBase): return
                for elemento in self._spo:
                    if(not elemento.startswith(chave)): continue
                    objecto = elemento.split(',')[2]
                    if not Graph.is_float_try(objecto): continue
                    if(not float(objecto) < float(valorBase)): continue
                    yield(elemento.split(','))
                return

            elif obj.startswith("[") and obj.endswith("]"):
                chave = str.format("%s,%s," %(sub, pred))
                intervaloInferior = float(obj.replace('[', '').replace(']', '').split(',')[0])
                intervaloSuperior = float(obj.replace('[', '').replace(']', '').split(',')[1])
                if not Graph.is_float_try(intervaloInferior): return
                if not Graph.is_float_try(intervaloSuperior): return
                for elemento in self._spo:
                    if(not elemento.startswith(chave)): continue
                    objecto = elemento.split(',')[2]
                    if not Graph.is_float_try(objecto): continue
                    if(float(objecto) < float(intervaloInferior) or float(objecto) > float(intervaloSuperior)): continue
                    yield(elemento.split(','))
                return

            elif obj.startswith("{TOP") and obj.endswith("}"):
                triplos = []

                chave = str.format("%s,%s," %(sub, pred))
                topNElementos = int(obj.replace('{TOP', '').replace('}', ''))
                if not Graph.is_float_try(topNElementos): return
                for elemento in self._spo:
                    if(not elemento.startswith(chave)): continue
                    objecto = elemento.split(',')[2]
                    if not Graph.is_float_try(objecto): continue
                    triplos.append([elemento.split(',')[0], elemento.split(',')[1], float(objecto)])
                    triplos = sorted(triplos, key=lambda obj: obj[2], reverse = True)
                    if not  triplos.__len__() > topNElementos: continue
                    for posicaoARemover in range(topNElementos, triplos.__len__()):
                        if posicaoARemover == triplos.__len__(): break
                        triplos.pop(posicaoARemover)
                for triplo in triplos:
                    yield(triplo[0], triplo[1], triplo[2])
                return

            chave = str.format("%s,%s,%s" %(sub, pred, obj))
            if not self._spo.__contains__(chave): return
            yield(chave.split(','))


        # I have all the info except for the obj
        elif sub != None and pred != None and obj == None:

            chave = str.format("%s,%s," %(sub, pred))
            for elemento in self._spo:
                matchObj = re.search(chave, elemento, re.M|re.I)
                if matchObj:
                   yield(elemento.split(','))


        # I have all the info except for the predicate
        elif sub != None and pred == None and obj != None:

            if obj.startswith(">"):
                chave = str.format("%s," %(sub))
                valorBase = float(obj.replace('>', ''))
                if not Graph.is_float_try(valorBase): return
                for elemento in self._spo:
                    if(not elemento.startswith(chave)): continue
                    objecto = elemento.split(',')[2]
                    if not Graph.is_float_try(objecto): continue
                    if(not float(objecto) > float(valorBase)): continue
                    yield(elemento.split(','))
                return

            elif obj.startswith("<"):
                chave = str.format("%s," %(sub))
                valorBase = float(obj.replace('<', ''))
                if not Graph.is_float_try(valorBase): return
                for elemento in self._spo:
                    if(not elemento.startswith(chave)): continue
                    objecto = elemento.split(',')[2]
                    if not Graph.is_float_try(objecto): continue
                    if(not float(objecto) < float(valorBase)): continue
                    yield(elemento.split(','))
                return


            elif obj.startswith("[") and obj.endswith("]"):
                chave = str.format("%s," %(sub))
                intervaloInferior = float(obj.replace('[', '').replace(']', '').split(',')[0])
                intervaloSuperior = float(obj.replace('[', '').replace(']', '').split(',')[1])
                if not Graph.is_float_try(intervaloInferior): return
                if not Graph.is_float_try(intervaloSuperior): return
                for elemento in self._spo:
                    if(not elemento.startswith(chave)): continue
                    objecto = elemento.split(',')[2]
                    if not Graph.is_float_try(objecto): continue
                    if(float(objecto) < float(intervaloInferior) or float(objecto) > float(intervaloSuperior)): continue
                    yield(elemento.split(','))
                return


            elif obj.startswith("{TOP") and obj.endswith("}"):
                triplos = []

                chave = str.format("%s," %(sub))
                topNElementos = int(obj.replace('{TOP', '').replace('}', ''))
                if not Graph.is_float_try(topNElementos): return
                for elemento in self._spo:
                    if(not elemento.startswith(chave)): continue
                    objecto = elemento.split(',')[2]
                    if not Graph.is_float_try(objecto): continue
                    triplos.append([elemento.split(',')[0], elemento.split(',')[1], float(objecto)])
                    triplos = sorted(triplos, key=lambda obj: obj[2], reverse = True)
                    if not  triplos.__len__() > topNElementos: continue
                    for posicaoARemover in range(topNElementos, triplos.__len__()):
                        if posicaoARemover == triplos.__len__(): break
                        triplos.pop(posicaoARemover)
                for triplo in triplos:
                    yield(triplo[0], triplo[1], triplo[2])
                return

            chave = str.format("%s,(.*),%s" % (sub, obj))
            for elemento in self._spo:
                matchObj = re.search(chave, elemento, re.M|re.I)
                if matchObj:
                   yield(elemento.split(','))


        # I have all the info except for the subject
        elif sub == None and pred != None and obj != None:

            if obj.startswith(">"):
                chave = str.format(",%s," %(pred))
                valorBase = float(obj.replace('>', ''))
                if not Graph.is_float_try(valorBase): return
                for elemento in self._spo:
                    matchObj = re.search(chave, elemento, re.M|re.I)
                    if matchObj:
                        objecto = elemento.split(',')[2]
                        if not Graph.is_float_try(objecto): continue
                        if(not float(objecto) > float(valorBase)): continue
                        yield(elemento.split(','))
                return

            elif obj.startswith("<"):
                chave = str.format(",%s," %(pred))
                valorBase = float(obj.replace('<', ''))
                if not Graph.is_float_try(valorBase): return
                for elemento in self._spo:
                    matchObj = re.search(chave, elemento, re.M|re.I)
                    if matchObj:
                        objecto = elemento.split(',')[2]
                        if not Graph.is_float_try(objecto): continue
                        if(not float(objecto) < float(valorBase)): continue
                        yield(elemento.split(','))
                return


            elif obj.startswith("[") and obj.endswith("]"):
                chave = str.format(",%s," %(pred))
                intervaloInferior = float(obj.replace('[', '').replace(']', '').split(',')[0])
                intervaloSuperior = float(obj.replace('[', '').replace(']', '').split(',')[1])
                if not Graph.is_float_try(intervaloInferior): return
                if not Graph.is_float_try(intervaloSuperior): return
                for elemento in self._spo:
                    matchObj = re.search(chave, elemento, re.M|re.I)
                    if matchObj:
                        objecto = elemento.split(',')[2]
                        if not Graph.is_float_try(objecto): continue
                        if(float(objecto) < float(intervaloInferior) or float(objecto) > float(intervaloSuperior)): continue
                        yield(elemento.split(','))
                return


            elif obj.startswith("{TOP") and obj.endswith("}"):
                triplos = []

                chave = str.format(",%s," %(pred))
                topNElementos = int(obj.replace('{TOP', '').replace('}', ''))
                if not Graph.is_float_try(topNElementos): return
                for elemento in self._spo:
                    matchObj = re.search(chave, elemento, re.M|re.I)
                    if matchObj:
                        objecto = elemento.split(',')[2]
                        if not Graph.is_float_try(objecto): continue
                        triplos.append([elemento.split(',')[0], elemento.split(',')[1], float(objecto)])
                        triplos = sorted(triplos, key=lambda obj: obj[2], reverse = True)
                        if not  triplos.__len__() > topNElementos: continue
                        for posicaoARemover in range(topNElementos, triplos.__len__()):
                            if posicaoARemover == triplos.__len__(): break
                            triplos.pop(posicaoARemover)
                for triplo in triplos:
                    yield(triplo[0], triplo[1], triplo[2])
                return

            chave = str.format(",%s,%s" %(pred, obj))
            for elemento in self._spo:
                if elemento.endswith(chave):
                   yield(elemento.split(','))


        # I only have object
        elif sub == None and pred == None and obj != None:

            if obj.startswith(">"):
                valorBase = float(obj.replace('>', ''))
                if not Graph.is_float_try(valorBase): return
                for elemento in self._spo:
                    objecto = elemento.split(',')[2]
                    if not Graph.is_float_try(objecto): continue
                    if(not float(objecto) > float(valorBase)): continue
                    yield(elemento.split(','))
                return

            elif obj.startswith("<"):
                valorBase = float(obj.replace('<', ''))
                if not Graph.is_float_try(valorBase): return
                for elemento in self._spo:
                    objecto = elemento.split(',')[2]
                    if not Graph.is_float_try(objecto): continue
                    if(not float(objecto) < float(valorBase)): continue
                    yield(elemento.split(','))
                return

            elif obj.startswith("[") and obj.endswith("]"):
                intervaloInferior = float(obj.replace('[', '').replace(']', '').split(',')[0])
                intervaloSuperior = float(obj.replace('[', '').replace(']', '').split(',')[1])
                if not Graph.is_float_try(intervaloInferior): return
                if not Graph.is_float_try(intervaloSuperior): return
                for elemento in self._spo:
                    objecto = elemento.split(',')[2]
                    if not Graph.is_float_try(objecto): continue
                    if(float(objecto) < float(intervaloInferior) or float(objecto) > float(intervaloSuperior)): continue
                    yield(elemento.split(','))
                return

            elif obj.startswith("{TOP") and obj.endswith("}"):
                triplos = []

                topNElementos = int(obj.replace('{TOP', '').replace('}', ''))
                if not Graph.is_float_try(topNElementos): return
                for elemento in self._spo:

                    objecto = elemento.split(',')[2]
                    if not Graph.is_float_try(objecto): continue
                    triplos.append([elemento.split(',')[0], elemento.split(',')[1], float(objecto)])
                    triplos = sorted(triplos, key=lambda obj: obj[2], reverse = True)
                    if not  triplos.__len__() > topNElementos: continue
                    for posicaoARemover in range(topNElementos, triplos.__len__()):
                        if posicaoARemover == triplos.__len__(): break
                        triplos.pop(posicaoARemover)
                for triplo in triplos:
                    yield(triplo[0], triplo[1], triplo[2])
                return

            chave = str.format(",%s" %obj)
            for elemento in self._spo:
                if elemento.endswith(chave):
                    yield(elemento.split(','))


        # I only have predicate
        elif sub == None and pred != None and obj == None:

            chave = str.format(",%s," %pred)

            for elemento in self._spo:
                matchObj = re.search(chave, elemento, re.M|re.I)
                if matchObj:
                   yield(elemento.split(','))


        # I only have subject
        elif sub != None and pred == None and obj == None:

            chave = str.format("%s," % sub)

            for elemento in self._spo:
                if elemento.startswith(chave):
                    yield(elemento.split(','))


        # I have no Info, I want everything
        elif sub == None and pred == None and obj == None:

            for elemento in self._spo:
                yield(elemento.split(','))

    def openFromCSV(self, FileName):
        self._spo = []

        out_file = open(FileName, "rt")
        for linha in out_file:
            elementos = linha.replace('\n', '').split(';')
            self.add(elementos[0], elementos[1], elementos[2])
        out_file.close()

    def is_float_try(self, str):
        try:
            float(str)
            return True
        except ValueError:
            return False

    def triplesToNt(self, filename, sub, pred, obj):
        out=open(filename + ".nt",'wt')
        for element in self.triples(sub, pred, obj):
            prefixo = "<SemanticWebMSI://"
            grafo = Graph()
            if grafo.is_float_try(element[2]):
                texto = prefixo + element[0] + "> " + prefixo + element[1] + "> " + "\"" + element[2] + "\"^^<http://www.w3.org/2001/XMLSchema#double> .\n"
            else:
                texto = prefixo + element[0] + "> " + prefixo + element[1] + "> " + "\"" + element[2] + "\"" + " .\n"
            out.write(texto)
            pass
        out.close()
        pass